/**
**************************************************************************************
*Palisma - Secrets of the Illuminati is an open-source 2D RPG                        *
*Copyright (C) 2006, Tony Sparks                                                     *
*                                                                                    *
*This library is free software; you can redistribute it and/or                       *
*modify it under the terms of the GNU Lesser General Public                          *
*License as published by the Free Software Foundation; either                        *
*version 2.1 of the License, or (at your option) any later version.                  *
*                                                                                    *
*This library is distributed in the hope that it will be useful,                     *
*but WITHOUT ANY WARRANTY; without even the implied warranty of                      *
*MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU                   *
*Lesser General Public License for more details.                                     *
*                                                                                    *
*You should have received a copy of the GNU Lesser General Public                    *
*License along with this library; if not, write to the Free Software                 *
*Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA      *
**************************************************************************************
*/
#include "StdAfx.h"
#include "../kernel.h"

//OpenGL headers 
#ifdef WIN32
#include <windows.h>
#endif

#include "GLRender.h"
#include <gl/glu.h>

extern Kernel* g_kernel;

#define MAX_SCREENIES 64

GLRender::GLRender(void)
{
    SetFogDensity( 0.43 );
    screen = NULL;
}

int GLRender::Init() 
{
    // TODO - Read in attributes from a file

    // Enable double buffering
    SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
    
    SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 32);

    SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); // 8
    SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
    SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
    SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);

    m_width = (int)g_kernel->GetCvars()->GetFloatValue("v_width");
    m_height = (int)g_kernel->GetCvars()->GetFloatValue("v_height");
    int fullscreen = (int)g_kernel->GetCvars()->GetFloatValue("v_fullscreen");

    if ( !m_width || ! m_height )
    {
        m_width = 800;
        m_height = 600;
    }
    if ( fullscreen )
        screen = SDL_SetVideoMode( m_width, m_height, 0, SDL_FULLSCREEN|SDL_OPENGL );
    else
        screen = SDL_SetVideoMode( m_width, m_height, 0, SDL_OPENGL );

    SDL_WM_SetCaption( (_PROGRAM_ + _VERSION_ ).c_str(), (_PROGRAM_+_VERSION_ ).c_str()  );

    /**
     * Initialize OpenGL
     */

    //Initialize OpenGL
	glClearColor(0.0, 0.0, 0.0, 0.0);	

	glEnable(GL_TEXTURE_2D);
	glEnable(GL_BLEND);
    
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    glDisable(GL_DEPTH_TEST);
    glDisable(GL_LIGHTING);
    // Disable cull facing for 2d QUADS
    glDisable(GL_CULL_FACE);

    glClearDepth(1.0);
    glViewport(0, 0, m_width, m_height);
    glMatrixMode(GL_PROJECTION);  
	glLoadIdentity();
    glOrtho(0.0f, m_width, m_height, 0.0f, -100.0f, 100.0f);
    // width and height of the game
    //gluOrtho2D(0.0, m_width, 0.0, m_height);
    
    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);

    //ChangeFogColor( 0.9,0.5,0.4);
    //EnableFog();
    
    const char * vender = (char*)glGetString(GL_VENDOR);
    std::string svender = vender;
    
    const char * version = (char*)glGetString(GL_VERSION);
    std::string sversion = version;

    const char * renderer = (char*)glGetString(GL_RENDERER);
    std::string srend = renderer;

    g_kernel->LPrintf("Video Driver Info:");
    g_kernel->LPrintf("==========================================================");
    g_kernel->LPrintf( ("VERSION: " + sversion).c_str() );
    g_kernel->LPrintf( ("Vendor: " + svender).c_str() );
    g_kernel->LPrintf( ("Renderer: " + srend).c_str() );

    Rect v( 0, 0, m_width, m_height);
    m_camera.SetViewPort( v );

    screen_f.renderer = this;
    g_kernel->GetConsole()->AddCommand( &screen_f, "screenshot" );
    if ( !m_fontManager.Create("serif.glf", "default" ) )
        return 1;

    m_fontManager.SetFontType( "default" );
    return 0;
}
void GLRender::Shutdown()
{
    // shut down the video subsystem
    SDL_QuitSubSystem( SDL_INIT_VIDEO );

}
void GLRender::DrawImage( Image* image, int x, int y)
{
    SetColor(1.0f, 1.0f, 1.0f, 1.0f);
    /**
     * Get the texture from the cache of images
     */
    glBindTexture(GL_TEXTURE_2D, image->textNum );

    if ( image->nBPP == 3 ) {
        glDisable(GL_BLEND);
        glDisable(GL_ALPHA_TEST);
    }
    glPushMatrix();
	glTranslatef( x, y, 0 );
    /**
     * draw the image out
     */
    glBegin(GL_QUADS);
            glTexCoord2f(0, 0);
            glVertex2f(0, 0);
            
            glTexCoord2f(0, 1 );
            glVertex2f(0, image->height);
            
            glTexCoord2f(1, 1);
            glVertex2f(image->width, image->height);

            glTexCoord2f(1, 0);
            glVertex2f(image->width, 0);
         
    glEnd();
    
    glEnable(GL_BLEND);
    glPopMatrix();
    // clean up
    SetColor(1.0f, 1.0f, 1.0f, 1.0f);

}

void GLRender::DrawImage( Image* image, int x, int y, float width, float height)
{

    SetColor(1.0f, 1.0f, 1.0f, 1.0f);
    /**
     * Get the texture from the cache of images
     */
    glBindTexture(GL_TEXTURE_2D, image->textNum );

    /**
     * draw the image out
     */
    if ( image->nBPP == 3 ) {
        glDisable(GL_BLEND);
        glDisable(GL_ALPHA_TEST);
    }
    glPushMatrix();
	glTranslatef( x, y, 0 );

    float heightt = (float)(height / image->height);
    float widtht = (float)(width / image->width);
    glBegin(GL_QUADS);
            glTexCoord2f(0,0);
            glVertex2f(0, 0);

            glTexCoord2f(0, heightt);
            glVertex2f(0, height/*+image->height*/);

            glTexCoord2f(widtht, heightt);
            glVertex2f(width/*+image->width*/, height/*+image->height*/);

            glTexCoord2f(widtht, 0);
            glVertex2f(width/*+image->width*/, 0);
        //glTexCoord2f(0, 0);
        //glVertex2f(0, 0);

        //glTexCoord2f(0, image->texHeight);
        //glVertex2f(0, height);

        //glTexCoord2f(image->texWidth, image->texHeight);
        //glVertex2f(width, height);

        //glTexCoord2f(image->texWidth, 0);
        //glVertex2f(width, 0);
    glEnd();
    glEnable(GL_BLEND);
    glPopMatrix();
    // clean up
    SetColor(1.0f, 1.0f, 1.0f, 1.0f);

}

void GLRender::DrawSubImage( Image* image, int x, int y, int row, int col)
{

    SetColor ( GetColorEffect() );
    //SetColorEffect(0.3f, 0.3f, 0.4f, 1.0f);
    /**
     * Get the texture from the cache of images
     */
    glBindTexture(GL_TEXTURE_2D, image->textNum );

    // find the multiplier
    float multH = 100.0f / image->rows; 
    float multW = 100.0f / image->columns;

    // mult by .01 to rid of rounding errors
    float texHeight  = (multH * row) * 0.01f; // row
    float texWidth   = (multW * col) * 0.01f; // col

    // find the offsets
    float xOff      = texWidth  - (multW * 0.01f);
    float yOff      = texHeight - (multH * 0.01f);
    /**
     * draw the image out
     */
    if ( image->nBPP == 3 ) {
        glDisable(GL_BLEND);
        glDisable(GL_ALPHA_TEST);
    }
    glPushMatrix();
	glTranslatef( x, y, 0 );

    glBegin(GL_QUADS);
        glTexCoord2f(xOff, yOff);
        glVertex2f(0, 0);

        glTexCoord2f(xOff, texHeight);
        glVertex2f(0, image->subHeight);

        glTexCoord2f(texWidth, texHeight);
        glVertex2f(image->subWidth, image->subHeight);

        glTexCoord2f(texWidth, yOff);
        glVertex2f(image->subWidth, 0);
    glEnd();

    glEnable(GL_BLEND);
    glPopMatrix();
    // clean up
    SetColor(1.0f, 1.0f, 1.0f, 1.0f);

}


void GLRender::DrawString( int x, int y, const char *s,...)
{

    char buffer[1024]; 
	va_list arguments;
    // parse out the arguments, and put them in 
    // string format
	va_start(arguments, s);
	      //  _vsnprintf_s(buffer, sizeof(buffer), s, arguments);
	    vsprintf(buffer, s, arguments);
    va_end(arguments);
    // Draw out text
    m_fontManager.DrawString( this, x, y, buffer );
}



void GLRender::DrawRect( int x, int y, int width, int height )
{
    //SetColor ( GetColorEffect() );

    glDisable(GL_TEXTURE_2D);
    glLineWidth(1.0f);

    glPushMatrix();
	glTranslatef( x, y, 0 );
    glBegin (GL_LINE_LOOP);
        // fill the area
        glVertex2f(0,0);
        glVertex2f(width, 0);
        glVertex2f(width, height);
        glVertex2f(0, height);
    glEnd();
    glPopMatrix();
    glEnable(GL_TEXTURE_2D); 

}

/** Draw a Circle */
void GLRender::DrawCircle( float radius, int x, int y)
{
    //SetColor ( GetColorEffect() );
    // disable 2d texturing for a moment
	glDisable(GL_TEXTURE_2D);
	glPushMatrix();
	glTranslatef( x, y, 0 );
	// draw a circle
	glBegin(GL_LINE_LOOP);
		for (double i=0.0; i<6.283; i+=0.06283)
			glVertex2f(radius*cos(i), radius*sin(i));
	glEnd();
	glPopMatrix();
    glEnable(GL_TEXTURE_2D);
}

void GLRender::DrawLine( const Vector2f &p1, const Vector2f &p2 )
{
    glDisable(GL_TEXTURE_2D);
    glLineWidth(1.0f);

    glPushMatrix();
	//glTranslatef( p1.x, p1.y, 0 );
    glBegin (GL_LINE_LOOP);
        // draw the line
        glVertex2f(p1.x, p1.y);
        glVertex2f(p2.x, p2.y);
    glEnd();
    //glPopMatrix();
    glEnable(GL_TEXTURE_2D); 
}

/** Fill a Circle */
void GLRender::FillCircle( float radius, int x, int y)
{
    //SetColor ( GetColorEffect() );
    // disable 2d texturing for a moment
	glDisable(GL_TEXTURE_2D);

	glPushMatrix();
	glTranslatef( x, y, 0 );
		
    // fill in a circle
	glBegin(GL_POLYGON);
		for (double i=0.0; i<6.283; i+=0.06283)
			glVertex2f(radius*cos(i), radius*sin(i));
	glEnd();

	glPopMatrix();
    glEnable(GL_TEXTURE_2D);
}

void GLRender::FillRect( int x, int y, int width, int height )
{
    //SetColor ( GetColorEffect() );

    glDisable(GL_TEXTURE_2D);
    glLineWidth(1.0f);

    glPushMatrix();
	glTranslatef( x, y, 0 );
    glBegin (GL_QUADS);
        // fill the area
        glVertex2f(0,0);
        glVertex2f(width, 0);
        glVertex2f(width, height);
        glVertex2f(0, height);
    glEnd();
    glPopMatrix();
    glEnable(GL_TEXTURE_2D);

}
void GLRender::SetColor( float r, float g, float b, float a)
{
    m_color.r = r; m_color.g = g; m_color.b = b; m_color.a = a;
    glColor4f(r, g, b, a);
}

void  GLRender::SetColor(Color c)
{
    SetColor( c.r, c.g, c.b, c.a );
}

Color GLRender::GetColor()
{
    return m_color;
}

void GLRender::SetColorEffect( float r, float g, float b, float a)
{
    m_cEffect.r = r; m_cEffect.g = g; m_cEffect.b = b; m_cEffect.a = a;
    glColor4f(r, g, b, a);
}

void  GLRender::SetColorEffect(Color c)
{
    SetColor( c.r, c.g, c.b, c.a );
}

Color GLRender::GetColorEffect()
{
    return m_cEffect;
}

void GLRender::PreRender()
{
    // clear the screen 
	glClear(GL_COLOR_BUFFER_BIT);

    
	//glTranslatef(0,0,-10);
    //glMatrixMode(GL_MODELVIEW);
	//glLoadIdentity();
    // make everything in this Frame
	//glPushMatrix();

    //static int time = 0;
    //static float value = 1.0f;
    //if ( g_kernel->GetTime() > time ) {
    //    glTranslatef(0,0, value++);
    //    //value -= 0.001f;
    //    //glScalef( value, value, 5 );
    //    time = g_kernel->GetTime() + 500;
    //}

    
}
void GLRender::PostRender()
{
	//glPopMatrix();			
    SDL_GL_SwapBuffers();
}

GLRender::~GLRender(void)
{
    g_kernel->GetConsole()->RemoveCommand( "screenshot" );
}




/** Enable fog */
void GLRender::EnableFog()
{
    glFogi(GL_FOG_MODE, GL_LINEAR );		// Fog Mode
    glFogfv(GL_FOG_COLOR, GetFogColor().GetData() );			// Set Fog Color
    glFogf(GL_FOG_DENSITY, m_fogDensity );				// How Dense Will The Fog Be
    glHint(GL_FOG_HINT, GL_NICEST);			// Fog Hint Value
    glFogf(GL_FOG_START, -10.0f);				// Fog Start Depth
    glFogf(GL_FOG_END, 10.0f);				// Fog End Depth
    glEnable(GL_FOG);					// Enables GL_FOG
}
void GLRender::DisableFog()
{
    glClearColor( 0,0,0,1);
    glDisable(GL_FOG);
}
void GLRender::SetFogDensity(float density)
{
    m_fogDensity = density;
}

void GLRender::ChangeFogColor(float r, float g, float b, float a)
{
    glClearColor( r, g, b, a );
    m_fogColor.SetData( r, g, b, a );
}

void GLRender::ChangeFogColor(Color c)
{
    ChangeFogColor( c.a, c.g, c.b, c.a );
}

Color GLRender::GetFogColor()
{
    return m_fogColor;
}


/** Take a screeny */
void GLRender::CaptureScreen(const std::string &file)
{
    int width = (int)g_kernel->GetCvars()->GetFloatValue("v_width");
    int height = (int)g_kernel->GetCvars()->GetFloatValue("v_height");

    if ( !width || ! height )
    {
        width = 800;
        height = 600;
    }   
    typedef unsigned char ubyte;
    int size = width * height * 3;
    ubyte* pixels = new unsigned char[ size ];
    if ( pixels == NULL )
        return;

    FILE* pFile = NULL;
    if ( file != "" )
    {
        pFile = fopen( (file + ".tga").c_str(), "wb" );
    } 
    else
    {
        int  ssNumber = 0;
        char fileName[64];
        while (ssNumber < MAX_SCREENIES && file == "" )
        {
            sprintf(fileName,"screenshot_%d.tga",ssNumber);
            pFile   = fopen(fileName,"rb");
            if ( pFile == NULL) 
            {
                pFile   = fopen(fileName,"wb");
                break;
            }
            else 
                fclose(pFile);
            
            ++ssNumber;
            if (ssNumber > MAX_SCREENIES-1)
            {
                g_kernel->LPrintf( "ERROR: Excided max allowed, try cleaning out your screenshot directory!" );
                return;
            }
        } 
    }
    
    // now copy whats on the screen
    glReadPixels( 0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, pixels );
    int i = 0;
    // convert to bgr
    while ( i < size )
    {
        ubyte tmpr = pixels[i];
        ubyte tmpb = pixels[i+2];

        pixels[i]   = tmpb;
        pixels[i+2] = tmpr;
        i += 3;
    }
    // fill the file with crap now...
    ubyte tgaheader[12]= { 0,0,2,0,0,0,0,0,0,0,0,0 };
    ubyte header[6] = { width % 256, width / 256, height % 256, height/256, 24, 0};
    
    fwrite(tgaheader, sizeof(ubyte), 12, pFile);
    fwrite(header, sizeof(ubyte), 6, pFile);
    fwrite(pixels, sizeof(ubyte), size, pFile);
    fclose(pFile);
        
    delete [] pixels; 

}